%{
#include <stdio.h>
#include <stdlib.h>
#include "llvm.h"
#include "ll.h"
#include "debug.h"
#include "tablesymb.h"
#include "ast_types.h"
#include "tabsym.h"
#include "tabfun.h"
#include "ctype.h"

#include "y.tab.h"

#define DEBUG 0

int next_lab = 0 ;
char new_lab() {
	return next_lab++;
}

int new_labi() {
	return next_lab++;
}

extern int lineno;
extern int column;

extern int next_num;

#define error(fmt, ...)                         \
    do { yyerror("error,");                     \
        fprintf(stderr, fmt, __VA_ARGS__);      \
        exit(1);                                \
    } while (0)


#define error_type(fmt, ...)                    \
    do { yyerror("type error,");                \
        fprintf(stderr, fmt, __VA_ARGS__);      \
        exit(1);                                \
    } while (0)

#define pp_maybe_str(s) s == NULL ? "NONE" : s

typedef enum {
    // In the top scope. all statements are allowed.
    st_top,

    // Inside a function. current_function will non-NULL. Class and function
    // declarations are not allowed.
    st_fun,

    // Inside a class. current_function will non-NULL. Only method declarations
    // are allowed.
    st_class,

    // Inside a method. Declaration of methods and functions are not allowed.
    st_method
} Parsing_state;

Symbol* checked_ts_add(tabsym *ts, char const* id, Type t);

void prepend(char* s, const char* t);
// Typechecking helpers
Type check_compop(char const* op, Type t1, Type t2);
Type check_numbinop(char const *op, Type t1, Type t2);
Type check_bbinop(char const *op, Type t1, Type t2);
void check_gen(char const* msg, Type t, Type expected);
Class* check_class_start(char* id, char* id_parent);
Fonction* check_def(char *id, ll_list* opt_params);
Symbol* check_assign(char const* id, Type te, bool *declaration);
bool check_reassign(Symbol* lhs_sym, Type trhs, char const* id);

void gen_lib();


// Certain declarations and statements are only possible in certain parsing
// states. This variable keeps track of the current parsing state.
Parsing_state pstate;

// These variables keeps track of the current class and/or method/function.
Fonction* current_func;
Class* current_class;

// Table of symbols
tabsym* ts;

// Table of functions
tabfun* tf;

// Table of classes
tabcls* tc;

// Code buckets. This is where we throw code generated on the fly that needs to
// be put at the bottom.
ll_list* code_bkt_btm;

%}

%union {
	int ival;
	float fval;
	Type type;
	Lhs_type lhs_type;
	char *sval;
       ll_list* list_params;
}

%token AND OR CLASS THEN ELSE END DO DEF LEQ GEQ
%token FLOAT INT TO RETURN IN NEQ LFALSE LTRUE
%token <sval> STRING ID CONSTID WHILE FOR
%token <ival> IF
%error-verbose
%type <ival> INT 
%type <ival> if
%type <fval> FLOAT
%type <lhs_type> lhs primary 
%type <lhs_type> expr comp_expr additive_expr multiplicative_expr
%type <list_params> opt_params params exprs
%type <sval> opt_heritage
%left '*' 
%left '/'
%left '+' '-'
%left '<' '>' LEQ GEQ EQ
%left AND OR

%%
program		:  {
    Fonction main;
    main.nom = "main";
    main.args = ll_create();
    main.ret_type = t_int;
    gen_start_func(&main, false);
} topstmts opt_terms {
    gen_ret(t_int, "0");
    gen_end_func();
}
;
topstmts        :      
| topstmt
| topstmts terms topstmt
;
topstmt	        : CLASS ID opt_heritage {
    Class* cl = check_class_start($2, $3);
    current_class = cl;
    pstate = st_class;
} body {
    pstate = st_top;
    current_class = NULL;
    ts_pops(ts);
} END
               | stmt
;
opt_heritage : /* none */ { $$ = NULL; }
             | '<' ID { $$ = $2; }

stmts            : /* none */
                | stmt
                | stmts terms stmt
                ;

if : IF {
    $1 = new_labi();
} expr THEN {
    // TODO: change name to check_generic
    check_gen("if-condition", t_bool, $3.type);
    ts_pushs(ts, sclvl_loc);

    gen_cond_br($3.sval, $1, "if_true", "if_false");
    gen_label("if_true", $1);
    $$ = $1;
}

stmt        : if body END {
    ts_pops(ts);

    gen_br($1, "if_false");
    gen_label("if_false", $1);
}
            | if body ELSE {
    ts_pops(ts);
    ts_pushs(ts, sclvl_loc);

    gen_br($1, "if_end");
    gen_label("if_false", $1);
} body END {
    ts_pops(ts);

    gen_br($1, "if_end");
    gen_label("if_end", $1);
}
            | FOR{ new_lab(); char *num = alloc();sprintf(num,"%d",next_lab);$1=num;} ID {
// TODO: an extra scope is added for the body later on, this is quite ugly.
    ts_pushs(ts, sclvl_loc);
    if (id_isconstant($3)) {
        error_type("for loop-counter '%s', cannot be constant", $3);
    }
    Symbol *sym = checked_ts_add(ts, $3, t_int); new_reg();$3 = alloc(); 
		sprintf($3,"r%d",next_num);
		printf("%%%s = alloca i32\n",$3);
		sym->reg = $3;		
		}
 IN expr{printf("store i32 %%%s, i32* %%%s\n",$6.sval,$3);} TO expr{new_reg(); gen_pre_for(next_num,$3,$9.sval,$1);gen_for(next_num,$1);} DO body { 
    ts_pops(ts); gen_end_for($1,$3);
} END {
    check_gen("range-start in for-in", t_int, $6.type);
    check_gen("range-end in for-in", t_int, $9.type);
}

                | WHILE {new_lab(); char *num = alloc();
	sprintf(num,"%d",next_lab);$1=num;gen_pre_while($1);} expr DO {gen_while($3.sval,$1);} body {gen_end_while($1);} END { check_gen("while-condition", t_bool, $3.type); }

                /* TODO: was lhs = expr */
                | ID '=' expr {

    bool declaration = false;
    Symbol* sym_lhs = check_assign($1, $3.type, &declaration);

    char* reg;
    if (declaration) {
        reg = alloc(); new_reg(); sprintf(reg,"r%d",next_num);
        printf("\t%%%s = alloca %s\n",
               reg, trub_to_tllvm(sym_lhs->type)
        );
        sym_lhs->reg = reg;
    } else {
        debug_print("symbols that are declared should be in a register%s\n", "");
        assert(sym_lhs->reg != NULL);
    }

    // now store the expression in the registry
    printf("\tstore %s %%%s, %s* %%%s\n",
           trub_to_tllvm(sym_lhs->type), $3.sval,
           trub_to_tllvm(sym_lhs->type), sym_lhs->reg
    );
}
                | expr
                | RETURN expr
{
    if (pstate != st_method && pstate != st_fun)
        error_type("return not allowed in this context%s\n","");

    current_func->ret_type = $2.type;
}
                | DEF ID opt_params
{
    Fonction* f = check_def($2, $3);
    pstate = pstate == st_top ? st_fun : st_method;
    current_func = f;
} body {
    // remove current function
    ts_pops(ts);
    debug_print("leaving function '%s'\n", current_func->nom);
    current_func = NULL;
    pstate = pstate == st_method ? st_class : st_top;
}

END
;

body  : term stmts terms
      | term
;

opt_params        : /* none */ { $$ = NULL; }
                | '(' ')' { $$ = NULL; }
                | '(' params ')' { $$ = $2; }
;
params            : ID ',' params { $$ = $3; ll_push($3, $1); }
                  | ID { $$ = ll_create(); ll_push($$, $1); }
; 
lhs
: ID {
    char *id = $1;
    Symbol* s = ts_lookup(ts, id);
    if (s != NULL) {
        debug_print("look up %s, found %s\n", id, pp_type(s->type));
        $$.type = s->type;
    } else {
        error_type("could not find type of variable '%s'\n", id);
        $$.type = t_no;
    }
    $$.sval = id;

    // now store the expression in the registry
    new_reg();$$.sval = alloc(); sprintf($$.sval,"r%d",next_num);
    printf("\t%%%s = load %s* %%%s\n",
           $$.sval, trub_to_tllvm(s->type), s->reg
    );
}
| ID '.' primary { }
| ID '(' exprs ')' {
    debug_print("funcall %s\n", $1);
    Fonction* f = tf_get(tf, $1);
    if (f) {
        debug_print("%s has type %s\n", f->nom, pp_type(f->ret_type));
        $$.type = f->ret_type;
    } else {
        error_type("could not find definition of '%s'\n", $1);
    }
	new_reg();$$.sval = alloc(); sprintf($$.sval,"r%d",next_num);
	gen_call(f, $3);
  }
;
exprs   : /* none */ { $$ = ll_create();}
		| exprs ',' expr {
            Lhs_type* lt = (Lhs_type*)malloc(sizeof(Lhs_type*));
            *lt = $3;
            $$ = $1;
            ll_push($$, &$3);
          }
        | expr {
            Lhs_type* lt = (Lhs_type*)malloc(sizeof(Lhs_type*));
            *lt = $1;
            $$ = ll_create();
            ll_push($$, lt);
          }
;

primary         : lhs {
    if ($1.type == t_no) {
        error_type("could not find type of variable '%s'\n", $1.sval);
    }
    $$.type = $1.type;
}
				| STRING {
    char *lbl = gen_next_lblstr();

    $$.type = t_chaine;

    // Declare constant string
    int len = (int)strlen($1) + 1;
    char *decl = (char*)malloc(sizeof(char)*100);
    sprintf(decl, "%s = constant [%d x i8] c\"%s\\00\"\n",
            lbl, len, $1);
    ll_push(code_bkt_btm, decl);

    // Get pointer to said string
    new_reg();
    $$.sval = alloc();
    sprintf($$.sval,"r%d",next_num);
    printf("\t%%r%d = getelementptr [%d x i8]* %s, i32 0, i32 0\n",
           next_num, len, lbl);

}
				| FLOAT { $$.type = t_float; new_reg();$$.sval = alloc(); sprintf($$.sval,"r%d",next_num);
					 printf("\t%%r%d = fadd double %f,0.0\n",next_num,$1); }
				| INT { $$.type = t_int; new_reg(); $$.sval = alloc();
                        sprintf($$.sval,"r%d",next_num); printf("\t%%r%d = add i32 %d,0\n",next_num,$1); }
				| LFALSE { $$.type = t_bool;new_reg(); 
					$$.sval = alloc();
					sprintf($$.sval,"r%d",next_num); printf("\t%%r%d = add i1 0,0\n",next_num);
					}
				| LTRUE { $$.type = t_bool;new_reg();
						$$.sval = alloc();
					sprintf($$.sval,"r%d",next_num); printf("\t%%r%d = add i1 0,1\n",next_num);
					}
				| '(' expr ')' { $$.type = $2.type; $$.sval=alloc(); strcpy($$.sval,$2.sval); }
;
expr            : expr AND comp_expr { $$.type = check_bbinop("&&", $1.type, $3.type);
					new_reg(); $$.sval=alloc(); sprintf($$.sval,"r%d",next_num);
					printf("\t%%r%d = and i1 %%%s,%%%s\n",next_num,$1.sval,$3.sval);
										
					}
                | expr OR comp_expr { $$.type = check_bbinop("||", $1.type, $3.type); 
					new_reg(); $$.sval=alloc(); sprintf($$.sval,"r%d",next_num);
					printf("\t%%r%d = or i1 %%%s,%%%s\n",next_num,$1.sval,$3.sval);
					}
                | comp_expr { $$.sval = alloc(); $$.sval = $1.sval; }
;
comp_expr       : additive_expr '<' additive_expr { $$.type = check_compop("<", $1.type, $3.type); //TODO :Si oprandes types different

						new_reg(); $$.sval=alloc(); sprintf($$.sval,"r%d",next_num);
							switch($1.type)
								{
									case t_int : printf("\t%%r%d = icmp ult i32 %%%s,%%%s\n",
											next_num,$1.sval,$3.sval);break;
									
									case t_float : printf("\t%%r%d = fcmp oeq double %%%s,%%%s\n",
											next_num,$1.sval,$3.sval);break;
								}

						 }
                | additive_expr '>' additive_expr { $$.type = check_compop(">", $1.type, $3.type);
							
						new_reg(); $$.sval=alloc(); sprintf($$.sval,"r%d",next_num);
							switch($1.type)
								{
									case t_int : printf("\t%%r%d = icmp ugt i32 %%%s,%%%s\n",
											next_num,$1.sval,$3.sval);break;
									
									case t_float : printf("\t%%r%d = fcmp ogt double %%%s,%%%s\n",
											next_num,$1.sval,$3.sval);break;
								}
						 }
                | additive_expr LEQ additive_expr { $$.type = check_compop("<=", $1.type, $3.type);
							new_reg(); $$.sval=alloc(); sprintf($$.sval,"r%d",next_num);
							switch($1.type)
								{
									case t_int : printf("\t%%r%d = icmp ule i32 %%%s,%%%s\n",
											next_num,$1.sval,$3.sval);break;
									
									case t_float : printf("\t%%r%d = fcmp ole double %%%s,%%%s\n",
											next_num,$1.sval,$3.sval);break;
								}
						 }
                | additive_expr GEQ additive_expr { $$.type = check_compop(">=", $1.type, $3.type);

							new_reg(); $$.sval=alloc(); sprintf($$.sval,"r%d",next_num);
							switch($1.type)
								{
									case t_int : printf("\t%%r%d = icmp uge i32 %%%s,%%%s\n",
											next_num,$1.sval,$3.sval);break;
									
									case t_float : printf("\t%%r%d = fcmp oge double %%%s,%%%s\n",
											next_num,$1.sval,$3.sval);break;
								}
						 }
                | additive_expr EQ additive_expr { $$.type = check_compop("==", $1.type, $3.type);
							new_reg(); $$.sval=alloc(); sprintf($$.sval,"r%d",next_num);
							switch($1.type)
								{
									case t_int : printf("\t%%r%d = icmp eq i32 %%%s,%%%s\n",
											next_num,$1.sval,$3.sval);break;
									
									case t_float : printf("\t%%r%d = fcmp oeq double %%%s,%%%s\n",
											next_num,$1.sval,$3.sval);break;
								}
						 }
                | additive_expr NEQ additive_expr { $$.type = check_compop("!=", $1.type, $3.type); 
							new_reg(); $$.sval=alloc(); sprintf($$.sval,"r%d",next_num);
							switch($1.type)
								{
									case t_int : printf("\t%%r%d = icmp ne i32 %%%s,%%%s\n",
											next_num,$1.sval,$3.sval);break;
									
									case t_float : printf("\t%%r%d = fcmp one double %%%s,%%%s\n",
											next_num,$1.sval,$3.sval);break;
								}
	
						}
                | additive_expr { $$.sval = alloc(); $$.sval = $1.sval; }
;
additive_expr   : multiplicative_expr { $$.sval = alloc(); $$.sval = $1.sval; }
		| additive_expr '+' multiplicative_expr {
                            $$.type = check_numbinop("+", $1.type, $3.type);
                            $$.sval = gen_numbinop('+', $$.type, &$1, &$3);
							  }
                | additive_expr '-' multiplicative_expr {
                            $$.type = check_numbinop("-", $1.type, $3.type);
                            $$.sval = gen_numbinop('-', $$.type, &$1, &$3);
							 }
;
multiplicative_expr : multiplicative_expr '*' primary {
                            $$.type = check_numbinop("*", $1.type, $3.type);
                            $$.sval = gen_numbinop('*', $$.type, &$1, &$3);
							 }
                    | multiplicative_expr '/' primary {
                            $$.type = check_numbinop("/", $1.type, $3.type);
                            $$.sval = gen_numbinop('/', $$.type, &$1, &$3);
							}
                    | primary { $$.sval = alloc(); strcpy($$.sval, $1.sval); }
;
opt_terms    : /* none */
        | terms
        ;

terms        : terms ';'
            | terms '\n'
            | ';'
            | '\n'
            ;
term            : ';'
                | '\n'
;
%%
int main() {
    code_bkt_btm = ll_create();

    // Set parsing state
    pstate = st_top;
    current_class = NULL;
    current_func = NULL;

    // Create symbol, function and class table
    ts = ts_create();
    tf = tf_create();
    tc = tc_create();

    // Create global scope
    ts_pushs(ts, sclvl_glob);

    // Create library, containing functions put*
    gen_lib();

    int ret = yyparse();

    // Pop global scope
    ts_pops(ts);

    ts = ts_delete(ts);
    // TODO: these functions should exist
    /*
     * tf = tf_delete(ts);
     * tc = tc_delete(ts);
     */

    // Output declarations for the bottom
    int i, size = ll_size(code_bkt_btm);
    for (i = 0; i < size; ++i) {
        puts((char*)ll_get(code_bkt_btm, i));
    }

    return ret;
}

void prepend(char* s, const char* t)
{
    size_t len = strlen(t);
    size_t i;

    memmove(s + len, s, len + 1);

    for (i = 0; i < len; ++i)
    {
        s[i] = t[i];
    }
}

void gen_fonc_inline(Type ret, char *nom, Type arg, char const* code) {
    Fonction* f = (Fonction*)malloc(sizeof(Fonction));
    f->nom = nom;
    f->args = ll_create();
    f->ret_type = ret;
    Type targ = arg;
    ll_push(f->args, &targ);
    tf_add(tf, nom, f);

    puts(code);
}

void gen_lib() {
    ll_list* pprintf = ll_create();
    Type tp1 = t_chaine;
    ll_push(pprintf, &tp1);
    gen_fdecl(t_int, "printf", pprintf, true);

    // Declare function puts
    ll_list* pputs = ll_create();
    Type arg_puts = t_chaine;
    ll_push(pputs, &arg_puts);
    gen_fdecl(t_int, "puts", pputs, false);

    // Put puts the function scope
    Fonction* f = (Fonction*)malloc(sizeof(Fonction));
    f->nom = "puts";
    f->args = pputs;
    f->ret_type = t_int;
    tf_add(tf, "puts", f);

    // puti
    gen_fonc_inline(
        t_void, "puti", t_int,
        "@puti_fmt = constant [ 4 x i8 ] c\"%d\\0A\\00\"\n"
        "define void @puti(i32 %x) {\n"
        "\tcall i32 (i8*, ...)* "
        "@printf(i8* getelementptr ([4 x i8]* @puti_fmt, i32 0, i32 0), i32 %x)\n"
        "\tret void\n"
        "}\n"
    );

    // puti
    gen_fonc_inline(
        t_void, "putf", t_int,
        "@putf_fmt = constant [ 4 x i8 ] c\"%f\\0A\\00\"\n"
        "define void @putf(double %x) {\n"
        "\tcall i32 (i8*, ...)* "
        "@printf(i8* getelementptr ([4 x i8]* @putf_fmt, i32 0, i32 0), double %x)\n"
        "\tret void\n"
        "}\n"
    );
}

Symbol* checked_ts_add(tabsym *ts, char const* id, Type t) {
    Nature n = id_getnature(id);
    if (n == n_var_inst && pstate != st_method) {
        error_type("cannot assign to instance variable %s in this context\n",
                   id);
    } else {
        return ts_add(ts, id, t);
    }
}

Type check_numbinop(char const* op, Type t1, Type t2) {
    debug_print("%s %s %s\n", pp_type(t1), op, pp_type(t2));
    if (t_isnumeric(t1) && t_isnumeric(t2)) {
        return (t1 == t_float || t2 == t_float)
            ? t_float
            : t_int;
    } else {
        char *msg = "in op %s, expected %s to be int or float, was %s\n";
        if (!t_isnumeric(t1))
            error_type(msg, op, "rhs", pp_type(t1));
        if (!t_isnumeric(t2))
            error_type(msg, op, "lhs", pp_type(t2));
    }
}

Type check_compop(char const* op, Type t1, Type t2) {
    debug_print("%s %s %s\n", pp_type(t1), op, pp_type(t2));
    check_numbinop(op, t1, t2);
    return t_bool;
}

Type check_bbinop(char const* op, Type t1, Type t2) {
    debug_print("%s %s %s\n", pp_type(t1), op, pp_type(t2));
    if (t1 == t_bool && t2 == t_bool) {
        return t_bool;
    } else {
        char *msg = "in op %s, expected %s to be bool, was %s\n";
        if (t1 != t_bool)
            error_type(msg, op, "lhs", pp_type(t1));
        if (t2 != t_bool)
            error_type(msg, op, "rhs", pp_type(t2));
    }
}

void check_gen(char const* msg, Type expected, Type t) {
    if (t != expected) {
        error_type("%s must be %s, found %s\n",
                   msg, pp_type(expected), pp_type(t));
    }
}

Symbol* check_assign(char const *id, Type te, bool *declaration) {
    Type tlhs;
    Symbol* lhs_sym;

    *declaration = false;

    // is id global, instance variable or local?
    Nature n = id_getnature(id);
    debug_print("%s = expr\n", id);

    if (n != n_var_loc) {
        // ts_lookup will look in the correct scopes depending on the nature of
        // id

        lhs_sym = ts_lookup(ts, id);
        if (lhs_sym == NULL) {
            *declaration = true;
        } else {
            check_reassign(lhs_sym, te, id);
            tlhs = lhs_sym->type;
        }
    } else {
        // Does lhs exist in the current scope ?
        if (lhs_sym = ts_lookup_top(ts, id)) {
            tlhs = lhs_sym->type;
            debug_print("found %s in top scope with type: %s\n",
                        id, pp_type(tlhs));
            check_reassign(lhs_sym, te, id);
        } else {
            debug_print("could not find %s in top scope\n", id);

            // does lhs exist in a higher scope with the type of expr?
            lhs_sym = ts_lookup(ts, id);
            if (lhs_sym && check_reassign(lhs_sym, te, id)) {
                tlhs = te;
            } else {
                debug_print("could not find %s in any scope\n", id);
                *declaration = true;
            }
        }
    }

    // ts_add adds id in the right context, and sets the constant flag
    // depending on the name id
    if (*declaration) {
        debug_print("declaring %s\n", id);
        lhs_sym = checked_ts_add(ts, id, te);
        tlhs = te;
    }

    assert(lhs_sym != NULL);

    return lhs_sym;
}

bool check_reassign(Symbol* lhs_sym, Type trhs, char const* id) {
    // Does lhs has the type of expr
    Type tlhs = lhs_sym->type;

    if (tlhs != trhs) {
        // if not type error
        error_type("cannot assign %s to variable %s of type %s\n",
                   pp_type(trhs), id, pp_type(tlhs));
    } else if (tlhs == t_chaine) {
        error_type("cannot re-assign to the string variable '%s'\n", id);
    } else if (lhs_sym->constant) {
        error_type("cannot re-assign to the constant variable '%s'\n", id);
    } else {
        return true;
    }

    return false;
}

Class* check_class_start(char* id, char* id_parent) {
    Class* cl;

    if (!id_isclass(id)) {
        error_type("Class name '%s' must start with majescule\n", id);
    }

    if (cl = tc_get(tc, id)) {
        // Check for situations like: A; A < B or A < B1; A < B2, or A < B; A.
        char *id_prev_parent = cl->parent == NULL ? NULL : cl->parent->nom;
        char *emsg = "confused regarding the parent of '%s', is it '%s' or '%s'?\n";
        if ((id_prev_parent != NULL && id_parent != NULL
             && !strcmp(id_prev_parent, id_parent))
            || (id_prev_parent != id_parent)
        ) {
            error_type(emsg, id, pp_maybe_str(id_prev_parent), pp_maybe_str(id_parent));
        }

    } else {
        Class* cl_parent = NULL;
        if (id_parent != NULL && (cl_parent = tc_get(tc, id_parent)) == NULL) {
            error_type("parent class %s of %s is not defined\n", id_parent, id);
        }

        debug_print("adding class '%s', child of '%s'\n"
                    , id, id_parent == NULL ? "none" : id_parent);

        cl = (Class*)malloc(sizeof(Class));
        // TODO: verify that this kind of affection is correct in regards to memory.
        cl->nom = id;
        cl->parent = cl_parent;
        cl->tf = tf_create();
        tc_add(tc, id, cl);
    }

    ts_pushs(ts, sclvl_inst);

    return cl;
}

Fonction* check_def(char *id, ll_list* opt_params) {
    tabfun* f_tf;
    Class* class;

    debug_print("trying to add function '%s' to class '%s'\n",
                id, !current_class ? "none" : current_class->nom);

    if (pstate == st_top) {
        // Add the function if it is not already defined in this scope.
        debug_print("in top%s\n","");
        if (tf_get(tf, id) != NULL) {
            error_type("function '%s' is already defined\n", id);
        }
    } else if (pstate == st_class) {
        // add the function if it doesnt already exist in this class, or in any
        // classes we inherit from
        debug_print("in class%s\n","");
        Fonction* m;
        if ((m = cl_get_method(current_class, id)) != NULL) {
            error_type("method '%s' is already defined in class '%s', "
                       "previous declaration in class '%s'\n"
                       , id, current_class->nom, m->class->nom);
        }

    } else {
        error_type("cant define inner function '%s' in function '%s'\n",
                   id, current_func->nom);
    }

    // Add function to class or global function table
    debug_print("adding function '%s' to class '%s'\n",
                id, !current_class ? "none" : current_class->nom);
    Fonction* f = (Fonction*)malloc(sizeof(Fonction));
    f->nom = id;
    f->class = current_class;
    tf_add(current_class ? current_class->tf : tf, id, f);

    ts_pushs(ts, sclvl_fun);

    if (opt_params != NULL) {
        char *arg = NULL;
        while (arg = ll_pop(opt_params)) {
            debug_print("adding '%s' to scope\n", arg);
            if (ts_lookup_top(ts,arg) != NULL) {
                error_type("two parameters named '%s' in function '%s'\n",
                           arg, f->nom);
            } else if (id_getnature(arg) != n_var_loc) {
                error_type("invalid parameter name '%s' in function '%s'\n",
                           arg, f->nom);
            } else {
                ts_add(ts, arg, t_no);
            }
        }
    }

    return f;
}
